package com.yixing.tech.wcs.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yixing.tech.common.autoline.domain.StackerCraneWCSTaskRequest;
import com.yixing.tech.common.entity.wcs.WCSTaskRequest;
import com.yixing.tech.common.base.BusinessException;
import com.yixing.tech.common.entity.wcs.TaskVO;
import com.yixing.tech.common.enums.YesOrNo;
import com.yixing.tech.common.autoline.constant.ResultStatus;
import com.yixing.tech.common.autoline.domain.AGVRasterRequest;
import com.yixing.tech.common.autoline.domain.WCSResponse;
import com.yixing.tech.common.autoline.task.WCSTaskUtil;
import com.yixing.tech.common.hikservice.rcs.RCSResponse;
import com.yixing.tech.common.hikservice.rcs.RCSTaskRequest;
import com.yixing.tech.common.hikservice.rcs.RCSUtil;
import com.yixing.tech.common.utils.HttpClientUtil;
import com.yixing.tech.common.utils.HttpResult;
import com.yixing.tech.wcs.constant.*;
import com.yixing.tech.wcs.domain.dto.AreaMappingDTO;
import com.yixing.tech.wcs.domain.po.*;
import com.yixing.tech.wcs.domain.vo.RCSTaskRequestVO;
import com.yixing.tech.wcs.domain.vo.SubTaskVO;
import com.yixing.tech.common.entity.WCSTaskCallbackVO;
import com.yixing.tech.wcs.service.ICRUDService;
import com.yixing.tech.wcs.service.TaskNotifyService;
import com.yixing.tech.wcs.service.ITaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.assertj.core.util.Lists;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TaskServiceImpl implements ITaskService {

    @Value("${tenant-id: 6204cb763da841718ddd1cf8}")
    private String tenantId;

    @Resource
    MongoTemplate mongoTemplate;

    @Resource
    ICRUDService crudService;

    @Autowired
    private TaskNotifyService taskNotifyService;

    /**
     * 任务信息保存规则：
     * 1.如果任务编号为空，WCS生成全局唯一串作为任务编号，并保存任务信息
     * 2.如果任务编号不为空，WCS判断改任务编号对应的任务信息是否存在，如果不存在，保存任务信息
     * */
    @Override
    public Object genTask(TaskVO taskVO) throws BusinessException {
        /** 校验起点库位是否存在 */
        Map<String, Object> map = new HashMap<>();
        map.put("code", taskVO.getStartLoc());
        if(!crudService.exists(map, EntityTemplateId.LOC_TEMPLATE)) {
            throw new BusinessException("起始库位信息不存在");
        }

        /** 校验终点库位/区域是否存在 */
        map.put("code", taskVO.getEndLoc());
        if(!crudService.exists(map, EntityTemplateId.LOC_TEMPLATE)) {
            throw new BusinessException("终点库位信息不存在");
        }

        if(ObjectUtils.isEmpty(taskVO.getEndLoc()) && ObjectUtils.isNotEmpty(taskVO.getEndArea())) {
            map.put("code", taskVO.getEndArea());
            if(!crudService.exists(map, EntityTemplateId.AREA_TEMPLATE)) {
                throw new BusinessException("终点库区信息不存在");
            }
        }

        /** 校验容器是否存在 */
        if(ObjectUtils.isNotEmpty(taskVO.getPodCode())) {
            map.put("code", taskVO.getPodCode());
            if(!crudService.exists(map, EntityTemplateId.POD_TEMPLATE)) {
                throw new BusinessException("容器信息不存在");
            }
        }

        /** 校验任务编号是否为空 */
        if(ObjectUtils.isEmpty(taskVO.getTaskCode())) {
            taskVO.setTaskCode(UUID.randomUUID().toString().replaceAll("-", ""));
        }
        map = new HashMap<>();
        map.put("taskCode", taskVO.getTaskCode());
        if(!crudService.exists(map, EntityTemplateId.TASK_TEMPLATE)) {
            saveTaskInfo(taskVO);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("msg", "下发任务成功!");
        return result;
    }

    public void saveTaskInfo(TaskVO task) throws BusinessException {
        Map<String, Object> taskMap = new HashMap<>();
        taskMap.put("taskCode", task.getTaskCode());
        taskMap.put("taskType", ObjectUtils.isNotEmpty(task.getTaskType()) ? Document.parse(String.format("{id: ObjectId(\"%s\")}", task.getTaskType())) : "");
        taskMap.put("startLoc", task.getStartLoc());
        taskMap.put("endLoc", task.getEndLoc());
        taskMap.put("startArea", task.getStartArea());
        taskMap.put("endArea", task.getEndArea());
        taskMap.put("podCode", task.getPodCode());
        taskMap.put("status", Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.DataStatus.VALID)));
        taskMap.put("performStatus", Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.UNISSUED)));
        taskMap.put("priority", ObjectUtils.isEmpty(task.getPriority()) ? 3 : task.getPriority());
        taskMap.put("operationNo", task.getOperationNo());

//        if(ObjectUtils.isNotEmpty( RequestContextHolder.getRequestAttributes())) {
//            HttpServletRequest request =  ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//            UserVO userVO = userUtil.getLoginUser(request.getHeader("x-user"));
//
//            taskMap.put("dispatcher", userVO.getName());    // TODO 获取当前用户信息
//            taskMap.put("dispatchTime", new Date());
//
//            taskMap.put("createId", userVO.getId());
//            taskMap.put("createName", userVO.getName());
//        }

        taskMap.put("tenantId", tenantId);
        taskMap.put("isDelete", YesOrNo.NO.ordinal());
        taskMap.put("createTime", new Date());

        mongoTemplate.save(taskMap, EntityTemplateId.TASK_TEMPLATE);
    }

    @Override
    public Object cancelTask(TaskVO taskVO) {
        return null;
    }

    @Override
    public Object cancelSubTask(SubTaskVO subTaskVO) {
        return null;
    }

    /**
     * 分解任务: 将WMS下发的任务分解成一个或多个子任务
     * 子任务机器人类型判定规则：
     * 1.起点任务：以起点所在的库区类型决定机器人类型
     * 2.终点任务：以终点所在的库区类型决定机器人类型
     * 3.中间点任务：以起点所在的库区类型决定机器人类型
     *
     * 在获取库位资源时，先获取起点和终点的资源，再获取中间点位的资源
     *
     * 该方法根据库区映射规则，将一个主任务分解为可能的多个子任务序列
     * 对于跨区域的任务，需要生成多个连接的子任务来完成整个流程
     * */
    @Override
    public void decomposeTask(TaskVO taskVO) throws BusinessException {
        /** 查询任务的起点和任务终点所在的库区编号与库区映射规则的记录进行匹配 */
        String startArea = taskVO.getStartArea(), endArea = taskVO.getEndArea();

        TaskTypeEnum taskType = TaskTypeEnum.getFromCode(taskVO.getTaskType());
        if(ObjectUtils.isEmpty(taskType))
            throw new BusinessException("任务类型为空");
        AreaMappingDTO areaMappingDTO = crudService.queryAreaMapping(taskType.getName(), startArea, endArea);
        if(ObjectUtils.isEmpty(areaMappingDTO)) {
            throw new BusinessException("库区映射规则未配置");
        }

        /** 校验是否任务是否已被分解 */
        if(crudService.existsSubTask(taskVO.getTaskCode())) {
            throw new BusinessException(String.format("任务[%s]已分解子任务", taskVO.getTaskCode()));
        }

        List<SubTask> subTaskList = new ArrayList<>();

        if(areaMappingDTO.getMappingPath().size() <= 2) {

            String areaCode = areaMappingDTO.getStartArea();
            Area area = crudService.queryAreaByCode(areaMappingDTO.getEndArea());
            if(DictId.AreaAutoType.STACKER_AREA.equals(area.getAutoType())) {
                areaCode = area.getCode();
            }

            SubTask subTask = createSubTask(areaCode, taskVO.getTaskType(), taskVO.getTaskCode(), taskVO.getPodCode(),
                    taskVO.getStartLoc(), taskVO.getEndLoc(), areaMappingDTO.getStartArea(), areaMappingDTO.getEndArea());

//            crudService.saveSubTask(subTask);
            log.info("任务[{}]分解得到的子任务为=======================", taskVO.getTaskCode());
            log.info(subTask.toString());

            subTaskList.add(subTask);
        }else {
            /** 第一个任务的机器人类型由起点库区的类型决定，剩余任务的机器人类型由终点库区的类型决定 */
            SubTask firstSubTask = createSubTask(areaMappingDTO.getMappingPath().get(0), taskVO.getTaskType(), taskVO.getTaskCode(), taskVO.getPodCode(),
                    taskVO.getStartLoc(), "", areaMappingDTO.getMappingPath().get(0), areaMappingDTO.getMappingPath().get(1));
            subTaskList.add(firstSubTask);

            for(int i=1; i+1 < areaMappingDTO.getMappingPath().size(); ++i) {
                SubTask subTask = createSubTask(areaMappingDTO.getMappingPath().get(i+1), taskVO.getTaskType(), taskVO.getTaskCode(), taskVO.getPodCode(),
                        "", "", areaMappingDTO.getMappingPath().get(i), areaMappingDTO.getMappingPath().get(i+1));
                subTaskList.add(subTask);
            }

            subTaskList.get(subTaskList.size() - 1).setEndLoc(taskVO.getEndLoc());
            /** 关联子任务的前置任务 */
            for(int i=1; i<subTaskList.size(); ++i) {
                SubTask forwardTask = subTaskList.get(i - 1);
                subTaskList.get(i).setForwardTask(forwardTask.getTaskCode());
            }

            log.info("任务[{}]分解得到的子任务为=======================", taskVO.getTaskCode());
            subTaskList.forEach(subTask -> log.info(subTask.toString()));

        }

        /** TODO 处理堆垛机移库问题 */
//        resolveConflict(subTaskList);

        crudService.saveSubTask(subTaskList);
    }

    @Override
    public void decomposeTaskWithTaskCode(String taskCode) throws BusinessException {
        Map<String, Object> params = new HashMap<>();
        params.put("taskCode", taskCode);
        List<Task> taskList = crudService.listTaskInfo(params);

        TaskVO taskVO = new TaskVO();
        try {
            BeanUtils.copyProperties(taskVO, taskList.get(0));
        } catch (InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        decomposeTask(taskVO);
    }

    @Override
    public Object decomposeTask() throws BusinessException {
        List<AggregationOperation> operations = new ArrayList<>();
//        operations.add(Aggregation.match(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())));
        operations.add(Aggregation.match(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())
                .and("performStatus").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.UNISSUED)))));
        operations.add(Aggregation.addFields()
                .addFieldWithValue("taskType", ConvertOperators.ToString.toString("$taskType.id"))
                .addFieldWithValue("status", ConvertOperators.ToString.toString("$status.id"))
                .addFieldWithValue("performStatus", ConvertOperators.ToString.toString("$performStatus.id"))
                .build());
        operations.add(Aggregation.sort(Sort.Direction.ASC, "createTime"));
        operations.add(Aggregation.lookup(EntityTemplateId.SUB_TASK_TEMPLATE, "taskCode", "parentTask", "subTask"));
        operations.add(Aggregation.unwind("subTask", true));
        operations.add(Aggregation.match(Criteria.where("subTask").is(null)));

        AggregationResults<Task> results = mongoTemplate.aggregate(Aggregation.newAggregation(operations), EntityTemplateId.TASK_TEMPLATE, Task.class);

        List<Task> taskList = results.getMappedResults();
        log.info("未分解的任务如下=====================");
        taskList.forEach(task -> log.info(task.toString()));

        CompletableFuture[] futureArr = new CompletableFuture[taskList.size()];
        int i = 0;

        for(Task task: taskList) {
            if(ObjectUtils.isEmpty(task.getStartArea())) {
                Map<String, Object> kv = new HashMap<>();
                kv.put("code", task.getStartLoc());
                Location loc = crudService.findOne(kv, Location.class, EntityTemplateId.LOC_TEMPLATE);
                task.setStartArea(loc.getArea());
                // 更新起点区域编号
                Map<String, Object> q = new HashMap<>();
                Map<String, Object> u = new HashMap<>();
                q.put("taskCode", task.getTaskCode());
                u.put("startArea", task.getStartArea());
                crudService.update(q, u, EntityTemplateId.TASK_TEMPLATE);
            }

            if(ObjectUtils.isEmpty(task.getEndArea())) {
                Map<String, Object> kv = new HashMap<>();
                kv.put("code", task.getEndLoc());
                Location loc = crudService.findOne(kv, Location.class, EntityTemplateId.LOC_TEMPLATE);
                task.setEndArea(loc.getArea());
                // 更新起点区域编号
                Map<String, Object> q = new HashMap<>();
                Map<String, Object> u = new HashMap<>();
                q.put("taskCode", task.getTaskCode());
                u.put("endArea", task.getEndArea());
                crudService.update(q, u, EntityTemplateId.TASK_TEMPLATE);
            }

            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    TaskVO taskVO = new TaskVO();
                    BeanUtils.copyProperties(taskVO, task);
                    decomposeTask(taskVO);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            });
            futureArr[i++] = future;
        }
        CompletableFuture<Void> allFuture = CompletableFuture.allOf(futureArr);
        try {
            allFuture.get();
        } catch (Exception e) {
            log.error("执行分解子任务抛出异常: {}",e);
        }

        return taskList;
    }

    @Override
    public void allocateResourceForSubTask() throws BusinessException {
        List<AggregationOperation> operations = new ArrayList<>();

        operations.add(Aggregation.match(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())
                .and("performStatus").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.UNISSUED)))));

        operations.add(Aggregation.addFields()

                .addFieldWithValue("priority", ConditionalOperators.IfNull.ifNull("priority").then(3))
                .addFieldWithValue("taskType", ConvertOperators.ToString.toString("$taskType.id"))
                .addFieldWithValue("status", ConvertOperators.ToString.toString("$status.id"))
                .addFieldWithValue("performStatus", ConvertOperators.ToString.toString("$performStatus.id"))
                .addFieldWithValue("id", ConvertOperators.ToString.toString("$_id"))
                .build());

        operations.add(Aggregation.lookup(EntityTemplateId.SUB_TASK_TEMPLATE, "taskCode", "parentTask", "subTask"));
        operations.add(Aggregation.unwind("subTask", true));

        operations.add(Aggregation.match(Criteria.where("subTask.tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())));
        operations.add(Aggregation.addFields()
                .addFieldWithValue("subTask.robotType", ConvertOperators.ToString.toString("$subTask.robotType.id"))
                .addFieldWithValue("subTask.performStatus", ConvertOperators.ToString.toString("$subTask.performStatus.id"))
                .addFieldWithValue("subTask.taskType", ConvertOperators.ToString.toString("$subTask.taskType.id"))
                .build());

        operations.add(Aggregation.sort(Sort.Direction.ASC, "priority", "createTime"));
        operations.add(Aggregation.group("taskCode")
                .first("taskCode").as("taskCode")
                .first("taskType").as("taskType")
                .first("priority").as("priority")
                .first("status").as("status")
                .first("performStatus").as("performStatus")
                .first("startLoc").as("startLoc")
                .first("endLoc").as("endLoc")
                .first("startArea").as("startArea")
                .first("endArea").as("endArea")
                .first("podCode").as("podCode")
                .first("operationNo").as("operationNo")
                .first("dispatcher").as("dispatcher")
                .first("dispatchTime").as("dispatchTime")
                .push("subTask").as("subTaskList")
        );

        List<Task> taskList = mongoTemplate.aggregate(Aggregation.newAggregation(operations), EntityTemplateId.TASK_TEMPLATE, Task.class).getMappedResults();
        log.info("executeSubTask查询到的已分解任务信息==========={}", taskList.size());
        taskList.forEach(task -> log.info(task.toString()));

        /** 分配终点库位 */
        Map<String, List<Task>> taskListMap = taskList.stream()
                .filter(task -> ObjectUtils.isEmpty(task.getEndLoc()))
                .collect(Collectors.groupingBy(Task::getEndArea, Collectors.toList()));
//        .filter(task -> ObjectUtils.isEmpty(task.getEndLoc()))

        log.info(taskListMap.toString());
        taskListMap.forEach((endCode, list) -> {
            list.sort(new Comparator<Task>() {
                @Override
                public int compare(Task o1, Task o2) {
                    return o2.getPriority() - o1.getPriority();
                }
            });
        });

        Map<String, Object> q = new HashMap<>();
        Map<String, Object> u = new HashMap<>();
        taskListMap.forEach((endCode, list) -> {
            for(Task task: list) {
                if(ObjectUtils.isNotEmpty(task.getEndLoc()))
                    continue;
                try {
                    log.debug("尝试为子任务分配终点库位:taskCode[{}]", task.getTaskCode());
                    Location endLoc = crudService.allocateEmptyLoc(Lists.newArrayList(endCode));
                    if(ObjectUtils.isEmpty(endLoc)) {
                        log.debug("子任务分配终点失败:taskCode[{}],areaCode[{}]", task.getTaskCode(), endCode);
                        break;
                    }
                    task.setEndLoc(endLoc.getCode());   // 更新大任务的终点库位
                    SubTask lastSubTask = findLastSubTaskOfTask(task.getSubTaskList());
                    lastSubTask.setEndLoc(endLoc.getCode());    // 更新最后一个子任务的终点库位

                    q.put("taskCode", task.getTaskCode());
                    u.put("endLoc", endLoc.getCode());
                    long mc = crudService.update(q, u, EntityTemplateId.TASK_TEMPLATE);
                    log.debug("update task endLoc:taskCode[{}],endLoc[{}],mc[{}]", task.getTaskCode(), endLoc.getCode(), mc);

                    q.put("taskCode", lastSubTask.getTaskCode());
                    u.put("endLoc", endLoc.getCode());
                    mc = crudService.update(q, u, EntityTemplateId.SUB_TASK_TEMPLATE);
                    log.debug("update subTaskCode endLoc:taskCode[{}],endLoc[{}],mc[{}]", lastSubTask.getTaskCode(), endLoc.getCode(), mc);
                } catch (BusinessException e) {
                    log.error("推荐空库位抛出异常:{}",e);
                }
            }
        });

        taskListMap = taskList.stream()
                .filter(task -> ObjectUtils.isNotEmpty(task.getEndLoc()) && ObjectUtils.isNotEmpty(task.getEndArea()))
                .collect(Collectors.groupingBy(Task::getEndArea, Collectors.toList()));

        /** 分配中间点位置 单支任务链 */
        taskListMap.forEach((endCode, list) -> {
            boolean skip = false;
            for(Task task: list) {
                List<SubTask> sortedSubTask = new ArrayList<>();
                Map<String, SubTask> ptm = new HashMap<>();

                task.getSubTaskList().forEach(subTask -> {
                    ptm.put(subTask.getForwardTask(), subTask);
                });
                SubTask curSubTask = ptm.remove("");
                if(ObjectUtils.isEmpty(curSubTask))
                    curSubTask = ptm.remove(null);

                sortedSubTask.add(curSubTask);
                while(!ptm.isEmpty()) {
                    curSubTask = ptm.remove(curSubTask.getTaskCode());
                    sortedSubTask.add(curSubTask);
                }

                String lastLocCode = null;
                for(SubTask subTask: sortedSubTask) {
                    if(ObjectUtils.isNotEmpty(lastLocCode) && ObjectUtils.isEmpty(subTask.getStartLoc())) {
                        subTask.setStartLoc(lastLocCode);

                        Map<String, Object> qs = new HashMap<>();
                        Map<String, Object> us = new HashMap<>();
                        qs.put("taskCode", subTask.getTaskCode());
                        us.put("startLoc", subTask.getStartLoc());
                        try {
                            crudService.update(qs, us, EntityTemplateId.SUB_TASK_TEMPLATE);
                        } catch (BusinessException e) {
                            log.error("更新起点库位抛出异常:{}",e);
                        }
                    }
                    if(ObjectUtils.isEmpty(subTask.getEndLoc())) {
                        try {
                            Location endLoc = crudService.allocateEmptyLoc(Lists.newArrayList(subTask.getEndArea()));
                            if(ObjectUtils.isEmpty(endLoc)) {
                                skip = true;
                                break;
                            }
                            lastLocCode = endLoc.getCode();
                            subTask.setEndLoc(endLoc.getCode());

                            Map<String, Object> qs = new HashMap<>();
                            Map<String, Object> us = new HashMap<>();
                            qs.put("taskCode", subTask.getTaskCode());
                            us.put("endLoc", subTask.getEndLoc());

                            crudService.update(qs, us, EntityTemplateId.SUB_TASK_TEMPLATE);
                        } catch (BusinessException e) {
                            log.error("推荐空库位抛出异常:{}",e);
                        }
                    }
                }

                if(skip)
                    break;

                log.info("任务[{}]的子任务资源分配完成:{}", task.getTaskCode(), task.getSubTaskList());
                // 更新任务的状态为 已下达
                q.put("taskCode", task.getTaskCode());
                Map<String, Object> tu = new HashMap<>();
                tu.put("performStatus", Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.ISSUED)));
                try {
                    crudService.update(q, tu, EntityTemplateId.TASK_TEMPLATE);
                } catch (BusinessException e) {
                    log.error("更新任务状态抛出异常:taskCode[{}],targetStatus[{}]",q.get("taskCode"), DictId.TaskPerformStatus.ISSUED);
                }
                // 更新所有子任务的执行状态为 已下达
                for(SubTask subTask: sortedSubTask) {
                    try {
                        q.put("taskCode", subTask.getTaskCode());
                        crudService.update(q, tu, EntityTemplateId.SUB_TASK_TEMPLATE);
                    } catch (BusinessException e) {
                        log.error("更新子任务状态抛出异常:taskCode[{}],targetStatus[{}]",q.get("taskCode"), DictId.TaskPerformStatus.ISSUED);
                    }
                }
            }
        });

    }

    /** 查找已完全分配资源完成的子任务，下发任务 */
    public void startUpSubTask() throws BusinessException {
        /** 查询子任务信息，关联的大任务状态未已下达，且前置任务为空 */
        List<AggregationOperation> operations = new ArrayList<>();
        operations.add(Aggregation.match(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())
                .and("performStatus").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.ISSUED)))
                .orOperator(Criteria.where("forwardTask").exists(false), Criteria.where("forwardTask").in(null, ""))));

        operations.add(Aggregation.addFields()
                .addFieldWithValue("robotType", ConvertOperators.ToString.toString("$robotType.id"))
                .addFieldWithValue("performStatus", ConvertOperators.ToString.toString("$performStatus.id"))
                .addFieldWithValue("taskType", ConvertOperators.ToString.toString("$taskType.id"))
                .build());

        operations.add(Aggregation.lookup(EntityTemplateId.TASK_TEMPLATE, "parentTask", "taskCode", "parentTaskObj"));
        operations.add(Aggregation.unwind("parentTaskObj", true));
        operations.add(Aggregation.match(Criteria.where("parentTaskObj.tenantId").is(tenantId).and("parentTaskObj.isDelete").ne(YesOrNo.YES.ordinal())
                .and("parentTaskObj.performStatus").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.ISSUED)))));

        List<SubTask> subTaskList = mongoTemplate.aggregate(Aggregation.newAggregation(operations), EntityTemplateId.SUB_TASK_TEMPLATE, SubTask.class).getMappedResults();

        log.info("查找到待执行的子任务如下================");
        subTaskList.forEach(subTask -> log.info(subTask.toString()));

        executeSubTask(subTaskList);
    }

    private void executeSubTask(List<SubTask> subTaskList) throws BusinessException {

//        int i = 0;
        List<CompletableFuture<Void>> genFutureList = new ArrayList<>();
        List<CompletableFuture<Void>> ctuOutBoundFutureList = new ArrayList<>();
        List<CompletableFuture<Void>> ctuInBoundFutureList = new ArrayList<>();
        for(SubTask subTask: subTaskList) {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    if(!DictId.TaskPerformStatus.ISSUED.equals(subTask.getPerformStatus())){
                        log.info("跳过执行子任务:taskCode[{}],status[{}]", subTask.getTaskCode(), subTask.getPerformStatus());
                        return;
                    }
                    /** 执行冲突解决 */
                    List<SubTask> sortedSubTaskList = getSortedSubTaskList(subTask.getParentTask());
                    boolean issue = resolveConflict(sortedSubTaskList);
                    if(issue) {
                        executeSubTask(subTask);
                    }
                } catch (Exception e) {
                    log.error("请求执行子任务抛出异常: {}", e);
                }
            });

            if(DictId.BotType.CTU.equals(subTask.getRobotType()) && DictId.TaskType.STOCK_OUT.equals(subTask.getTaskType())) {
                ctuOutBoundFutureList.add(future);
            }else if(DictId.BotType.CTU.equals(subTask.getRobotType()) && DictId.TaskType.STOCK_IN.equals(subTask.getTaskType())) {
                ctuInBoundFutureList.add(future);
            }else {
                genFutureList.add(future);
            }
//            futureArr[i++] = future;
        }
        CompletableFuture[] futureArr = new CompletableFuture[genFutureList.size()];
        for(int i=0; i<genFutureList.size(); ++i)
            futureArr[i] = genFutureList.get(i);
        CompletableFuture<Void> allFuture = CompletableFuture.allOf(futureArr);
        try {
            allFuture.get();
            executeCTUSubTask(ctuOutBoundFutureList);
        } catch (ExecutionException | InterruptedException e) {
            log.error("请求执行子任务抛出异常: {}", e);
        }
    }

    private void executeCTUSubTask(List<CompletableFuture<Void>> ctuFutureList) throws ExecutionException, InterruptedException {
        synchronized (DictId.BotType.CTU) {
            // 检查当前是否有正在执行的CTU出库任务
            boolean existed = existRunningOutBoundTaskForCTU();
            if(ctuFutureList.size() == 0) {
                log.info("当前无可执行的CTU出库任务");
                return;
            }
            if(existed) {
                log.info("当前有未执行完成的CTU出库任务");
                return;
            }
            log.info("准备下发CTU出库任务:size[{}]", ctuFutureList.size());
            if(ctuFutureList.size() <= 7) {
                CompletableFuture[] futureArr = new CompletableFuture[ctuFutureList.size()];
                for(int i=0; i<ctuFutureList.size(); ++i)
                    futureArr[i] = ctuFutureList.get(i);

                CompletableFuture<Void> allFuture = CompletableFuture.allOf(futureArr);
                allFuture.get();
            }else {
                CompletableFuture[] futureArr = new CompletableFuture[7];
                for(int i=0; i < 7; ++i)
                    futureArr[i] = ctuFutureList.get(i);

                CompletableFuture<Void> allFuture = CompletableFuture.allOf(futureArr);
                allFuture.get();
            }
        }
    }

    private void executeCtuInBoundSubTask(List<CompletableFuture<Void>> ctuFutureList) throws ExecutionException, InterruptedException {

    }

    private boolean existRunningOutBoundTaskForCTU() {

        Query query = new Query(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())
                .and("robotType").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.BotType.CTU)))
                .and("taskType").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskType.STOCK_OUT)))
                .and("performStatus").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.RUNNING))));

        return mongoTemplate.exists(query, EntityTemplateId.SUB_TASK_TEMPLATE);
    }

    /** 处理堆垛机移库问题 */
    private boolean resolveConflict(List<SubTask> subTaskList) throws BusinessException {
        boolean issue = true;
        SubTask tempSubTask = subTaskList.get(0);
        // 处理第一个任务为移库的情况
        if(DictId.BotType.STACKER.equals(tempSubTask.getRobotType()) && ObjectUtils.isNotEmpty(tempSubTask.getEndLoc())) {
            Map<String, Object> q = new HashMap<>();
            q.put("code", tempSubTask.getEndLoc());
            Location loc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
            if(ObjectUtils.isNotEmpty(loc) && new Integer(2).equals(loc.getDeep()) && ObjectUtils.isNotEmpty(loc.getGroup())) {
                q = new HashMap<>();
                q.put("deep", 1);
                q.put("group", loc.getGroup());
                Location outerLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                // 检查移库目标库位的库位组是否存在浅库位，且浅库位被锁定, 不论浅库位是否存在货架，都需等待浅库位完成任务
                if(ObjectUtils.isNotEmpty(outerLoc) && ObjectUtils.isNotEmpty(outerLoc.getLock()) && outerLoc.getLock()) {
                    return false;
                }
            }
        }

        // 第一个子任务起点不为空，且机器人类型为堆垛机
        if(DictId.BotType.STACKER.equals(tempSubTask.getRobotType()) && ObjectUtils.isNotEmpty(tempSubTask.getStartLoc())) {
            Map<String, Object> q = new HashMap<>();
            q.put("code", tempSubTask.getStartLoc());
            Location loc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
            if(ObjectUtils.isNotEmpty(loc) && new Integer(2).equals(loc.getDeep()) && ObjectUtils.isNotEmpty(loc.getGroup())) {
                q = new HashMap<>();
                q.put("deep", 1);
                q.put("group", loc.getGroup());
                Location outerLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                if(ObjectUtils.isNotEmpty(outerLoc) && ObjectUtils.isNotEmpty(outerLoc.getLock()) && outerLoc.getLock()) {
                    boolean exist = crudService.existSubTaskUnfinishedByLocCode(outerLoc.getCode());
                    for(SubTask sTask: subTaskList) {
                        if(outerLoc.getCode().equals(sTask.getStartLoc()) || outerLoc.getCode().equals(sTask.getEndLoc()))
                            return true;
                    }
                    log.info("已存在浅层库位的子任务:{},exist[{}]", outerLoc, exist);
                    return false;
                }
                if(ObjectUtils.isNotEmpty(outerLoc) && ObjectUtils.isNotEmpty(outerLoc.getContainerCode())) {
                    // 如果子任务列表中已有浅层库位，说明已生成移库任务
                    for(SubTask sTask: subTaskList) {
                        if(outerLoc.getCode().equals(sTask.getStartLoc()) || outerLoc.getCode().equals(sTask.getEndLoc()))
                            return true;
                    }

                    /* 外层库位有料框，需要移库 */

                    // 尝试锁定浅层库位
                    long mc = crudService.lockLoc(outerLoc.getCode());
                    if(mc == 0)     // 锁定失败，不执行任务
                        return false;
                    else {
                        // 检查浅层库位是否绑定容器
                        outerLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                        if(ObjectUtils.isNotEmpty(outerLoc) && ObjectUtils.isNotEmpty(outerLoc.getContainerCode())) {
                            // 分配移库空库位 // 尝试获取一个空库位
                            Location nLoc = crudService.allocateEmptyLoc(Lists.newArrayList(outerLoc.getArea()));
                            if (ObjectUtils.isNotEmpty(nLoc)) {

                                // 检查新库位是否为深库位，且浅层库位也绑定容器  // 查看空库位是否为深库位，其对应浅库位S是否有容器
                                if(new Integer(2).equals(nLoc.getDeep()) && ObjectUtils.isNotEmpty(nLoc.getGroup())) {
                                    q = new HashMap<>();
                                    q.put("deep", 1);
                                    q.put("group", nLoc.getGroup());
                                    Location nOLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                                    if(ObjectUtils.isNotEmpty(nOLoc)) {
                                        mc = crudService.lockLoc(nOLoc.getCode());
                                        if(mc == 0) {
                                            // 解锁浅层库位、解锁新的空的深库位
                                            crudService.unlockLoc(nLoc.getCode());
                                            crudService.unlockLoc(outerLoc.getCode());
                                            return false;
                                        }
                                    }
                                    nOLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                                    if(ObjectUtils.isNotEmpty(nOLoc) && ObjectUtils.isNotEmpty(nOLoc.getContainerCode())) {
                                        // 浅库位S如果有容器，尝试锁定浅库位S
                                        // 尝试锁定浅层库位
//                                        mc = crudService.lockLoc(nOLoc.getCode());
//                                        if(mc == 0) {
//                                            // 解锁浅层库位、解锁新的空的深库位
//                                            crudService.unlockLoc(nLoc.getCode());
//                                            crudService.unlockLoc(outerLoc.getCode());
//                                            return false;
//                                        }else {
                                            nOLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                                            if(ObjectUtils.isNotEmpty(nOLoc) && ObjectUtils.isNotEmpty(nOLoc.getContainerCode())) {
                                                // 生成并保存两个移库任务
                                                SubTask subTask = createSubTask(outerLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                                        outerLoc.getContainerCode(), outerLoc.getCode(), nOLoc.getCode(), outerLoc.getArea(), nOLoc.getArea());
//                        tempSubTask.setForwardTask(subTask.getTaskCode());
                                                crudService.updateSubTaskForwardTaskCode(tempSubTask.getTaskCode(), subTask.getTaskCode());
                                                crudService.saveSubTask(subTask, DictId.TaskPerformStatus.ISSUED);

                                                SubTask subTask1 = createSubTask(nOLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                                        nOLoc.getContainerCode(), nOLoc.getCode(), nLoc.getCode(), nOLoc.getArea(), nLoc.getArea());
//                                                subTask1.setForwardTask(tempSubTask.getTaskCode());
                                                crudService.saveSubTask(subTask1, DictId.TaskPerformStatus.ISSUED);
                                                crudService.updateSubTaskForwardTaskCode(subTask.getTaskCode(), subTask1.getTaskCode());

//                                                if (subTaskList.size() > 1) {
//                                                    crudService.updateSubTaskForwardTaskCode(subTaskList.get(1).getTaskCode(), subTask1.getTaskCode());
//                                                }
                                                executeSubTask(subTask1);
                                                issue = false;
                                            }else {
                                                // 生成并保存一个移库任务
                                                SubTask subTask = createSubTask(outerLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                                        outerLoc.getContainerCode(), outerLoc.getCode(), nLoc.getCode(), outerLoc.getArea(), nLoc.getArea());
//                        tempSubTask.setForwardTask(subTask.getTaskCode());
                                                crudService.updateSubTaskForwardTaskCode(tempSubTask.getTaskCode(), subTask.getTaskCode());
                                                crudService.saveSubTask(subTask, DictId.TaskPerformStatus.ISSUED);

//                                                if (subTaskList.size() > 1) {
//                                                    crudService.updateSubTaskForwardTaskCode(subTaskList.get(1).getTaskCode(), subTask1.getTaskCode());
//                                                }
                                                executeSubTask(subTask);
                                                issue = false;
                                            }
//                                        }
                                    }else {
                                        if(ObjectUtils.isNotEmpty(nOLoc)) {
                                            // 解锁移库库位的浅库位
                                            crudService.unlockLoc(nOLoc.getCode());
                                        }
                                        // 生成并保存一个移库任务
                                        SubTask subTask = createSubTask(outerLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                                outerLoc.getContainerCode(), outerLoc.getCode(), nLoc.getCode(), outerLoc.getArea(), nLoc.getArea());
//                        tempSubTask.setForwardTask(subTask.getTaskCode());
                                        crudService.updateSubTaskForwardTaskCode(tempSubTask.getTaskCode(), subTask.getTaskCode());
                                        crudService.saveSubTask(subTask, DictId.TaskPerformStatus.ISSUED);

//                                                if (subTaskList.size() > 1) {
//                                                    crudService.updateSubTaskForwardTaskCode(subTaskList.get(1).getTaskCode(), subTask1.getTaskCode());
//                                                }
                                        executeSubTask(subTask);
                                        issue = false;
                                    }
                                }else {
                                    // 生成并保存一个移库任务
                                    SubTask subTask = createSubTask(outerLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                            outerLoc.getContainerCode(), outerLoc.getCode(), nLoc.getCode(), outerLoc.getArea(), nLoc.getArea());
//                        tempSubTask.setForwardTask(subTask.getTaskCode());
                                    crudService.updateSubTaskForwardTaskCode(tempSubTask.getTaskCode(), subTask.getTaskCode());
                                    crudService.saveSubTask(subTask, DictId.TaskPerformStatus.ISSUED);

//                                                if (subTaskList.size() > 1) {
//                                                    crudService.updateSubTaskForwardTaskCode(subTaskList.get(1).getTaskCode(), subTask1.getTaskCode());
//                                                }
                                    executeSubTask(subTask);
                                    issue = false;
                                }

                            } else {
                                // 解锁浅层库位
                                crudService.unlockLoc(outerLoc.getCode());
                                issue = false;
                            }
                        }else {
                            log.info("浅层库位无绑定容器，直接执行正常任务");
                            crudService.unlockLoc(outerLoc.getCode());
                            issue = true;
                        }
                    }
                    // 保存任务，设置前置任务信息

                }
            }
        }
        if(subTaskList.size() >= 2) {
            tempSubTask = subTaskList.get(subTaskList.size() - 1);
            // 最后一个任务终点不为空，且机器人类型为堆垛机
            if(DictId.BotType.STACKER.equals(tempSubTask.getRobotType()) && ObjectUtils.isNotEmpty(tempSubTask.getEndLoc())) {
                Map<String, Object> q = new HashMap<>();
                q.put("code", tempSubTask.getEndLoc());
                Location loc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                if(ObjectUtils.isNotEmpty(loc) && Integer.valueOf(2).equals(loc.getDeep()) && ObjectUtils.isNotEmpty(loc.getGroup())) {
                    q = new HashMap<>();
                    q.put("deep", 1);
                    q.put("group", loc.getGroup());
                    Location outerLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                    log.info("处理深浅库位,浅层库位信息:{}", outerLoc);

                    // 查看浅层库位是否被锁定
                    if(ObjectUtils.isNotEmpty(outerLoc) && ObjectUtils.isNotEmpty(outerLoc.getLock()) && outerLoc.getLock()) {
                        boolean exist = crudService.existSubTaskUnfinishedByLocCode(outerLoc.getCode());
                        for(SubTask sTask: subTaskList) {
                            if(outerLoc.getCode().equals(sTask.getStartLoc()) || outerLoc.getCode().equals(sTask.getEndLoc())) {
                                log.info("处理深浅库位,子任务列表已有浅层库位:{}", sTask);
                                return true;
                            }
                        }
                        if(ObjectUtils.isEmpty(outerLoc.getContainerCode())) {
                            if(crudService.existSubTaskRunningByLocCode(outerLoc.getCode())) {
                                // 如果浅库位已有正在执行的任务，不允许深库位执行
                                return false;
                            }
                            // 深浅库位同时入库，放行深库位入库任务
                            return true;
                        }
                        log.info("已存在浅层库位的子任务:{}", outerLoc);
                        return false;
                    }
                    if(ObjectUtils.isNotEmpty(outerLoc) && ObjectUtils.isNotEmpty(outerLoc.getContainerCode())) {
                        // 如果子任务列表中已有浅层库位，说明已生成移库任务
                        for(SubTask sTask: subTaskList) {
                            if(outerLoc.getCode().equals(sTask.getStartLoc()) || outerLoc.getCode().equals(sTask.getEndLoc())) {
                                log.info("处理深浅库位,子任务列表已有浅层库位:{}", sTask);
                                return true;
                            }
                        }

                        // 外层库位有料框，需要移库
                        // 尝试锁定浅层库位
                        long mc = crudService.lockLoc(outerLoc.getCode());
                        if(mc == 0)     // 锁定失败，不执行任务
                            return false;
                        else {
                            // 检查浅层库位是否绑定容器
                            outerLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                            if(ObjectUtils.isNotEmpty(outerLoc) && ObjectUtils.isNotEmpty(outerLoc.getContainerCode())) {
                                // 分配移库空库位 // 尝试获取一个空库位
                                Location nLoc = crudService.allocateEmptyLoc(Lists.newArrayList(outerLoc.getArea()));
                                if (ObjectUtils.isNotEmpty(nLoc)) {

                                    // 检查新库位是否为深库位，且浅层库位也绑定容器  // 查看空库位是否为深库位，其对应浅库位S是否有容器
                                    if(Integer.valueOf(2).equals(nLoc.getDeep()) && ObjectUtils.isNotEmpty(nLoc.getGroup())) {
                                        q = new HashMap<>();
                                        q.put("deep", 1);
                                        q.put("group", nLoc.getGroup());
                                        Location nOLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                                        if(ObjectUtils.isNotEmpty(nOLoc)) {
                                            mc = crudService.lockLoc(nOLoc.getCode());
                                            if(mc == 0) {
                                                // 解锁浅层库位、解锁新的空的深库位
                                                crudService.unlockLoc(nLoc.getCode());
                                                crudService.unlockLoc(outerLoc.getCode());
                                                return false;
                                            }
                                        }
                                        nOLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                                        if(ObjectUtils.isNotEmpty(nOLoc) && ObjectUtils.isNotEmpty(nOLoc.getContainerCode())) {
                                            // 浅库位S如果有容器，尝试锁定浅库位S
                                            // 尝试锁定浅层库位
//                                            mc = crudService.lockLoc(nOLoc.getCode());
//                                            if(mc == 0) {
//                                                // 解锁浅层库位、解锁新的空的深库位
//                                                crudService.unlockLoc(nLoc.getCode());
//                                                crudService.unlockLoc(outerLoc.getCode());
//                                                return false;
//                                            }else {
                                                nOLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                                                if(ObjectUtils.isNotEmpty(nOLoc) && ObjectUtils.isNotEmpty(nOLoc.getContainerCode())) {
//                                                    Location nLoc = crudService.allocateEmptyLoc(Lists.newArrayList(DictName.AreaCode.CONFLICT_AREA));
//                                                    if (ObjectUtils.isNotEmpty(nLoc)) {
                                                    // 生成并保存两个移库任务
                                                        SubTask subTask = createSubTask(outerLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                                                outerLoc.getContainerCode(), outerLoc.getCode(), nOLoc.getCode(), outerLoc.getArea(), nOLoc.getArea());
                                                        subTask.setForwardTask(tempSubTask.getForwardTask());
//                        tempSubTask.setForwardTask(subTask.getTaskCode());
                                                        crudService.updateSubTaskForwardTaskCode(tempSubTask.getTaskCode(), subTask.getTaskCode());
                                                        crudService.saveSubTask(subTask, DictId.TaskPerformStatus.ISSUED);

                                                        SubTask subTask1 = createSubTask(nOLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                                                nOLoc.getContainerCode(), nOLoc.getCode(), nLoc.getCode(), nOLoc.getArea(), nLoc.getArea());
                                                        subTask1.setForwardTask(subTask.getForwardTask());
                                                        crudService.saveSubTask(subTask1, DictId.TaskPerformStatus.ISSUED);
                                                        crudService.updateSubTaskForwardTaskCode(subTask.getTaskCode(), subTask1.getTaskCode());

                                                        issue = false;
//                                                    } else {
//                                                        issue = false;
//                                                    }
                                                }else {
                                                    // 生成并保存一个移库任务
                                                    SubTask subTask = createSubTask(outerLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                                            outerLoc.getContainerCode(), outerLoc.getCode(), nLoc.getCode(), outerLoc.getArea(), nLoc.getArea());
//                        tempSubTask.setForwardTask(subTask.getTaskCode());
                                                    subTask.setForwardTask(tempSubTask.getForwardTask());
                                                    crudService.updateSubTaskForwardTaskCode(tempSubTask.getTaskCode(), subTask.getTaskCode());
                                                    crudService.saveSubTask(subTask, DictId.TaskPerformStatus.ISSUED);
                                                    issue = false;
                                                }
//                                            }
                                        }else {
                                            if(ObjectUtils.isNotEmpty(nOLoc)) {
                                                // 解锁移库库位的浅库位
                                                crudService.unlockLoc(nOLoc.getCode());
                                            }
                                            // 生成并保存一个移库任务
                                            SubTask subTask = createSubTask(outerLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                                    outerLoc.getContainerCode(), outerLoc.getCode(), nLoc.getCode(), outerLoc.getArea(), nLoc.getArea());
//                        tempSubTask.setForwardTask(subTask.getTaskCode());
                                            subTask.setForwardTask(tempSubTask.getForwardTask());
                                            crudService.updateSubTaskForwardTaskCode(tempSubTask.getTaskCode(), subTask.getTaskCode());
                                            crudService.saveSubTask(subTask, DictId.TaskPerformStatus.ISSUED);
                                            issue = false;
                                        }
                                    }else {
                                        // 生成并保存一个移库任务
                                        SubTask subTask = createSubTask(outerLoc.getArea(), DictId.TaskType.MOVING, tempSubTask.getParentTask(),
                                                outerLoc.getContainerCode(), outerLoc.getCode(), nLoc.getCode(), outerLoc.getArea(), nLoc.getArea());
//                        tempSubTask.setForwardTask(subTask.getTaskCode());
                                        subTask.setForwardTask(tempSubTask.getForwardTask());
                                        crudService.updateSubTaskForwardTaskCode(tempSubTask.getTaskCode(), subTask.getTaskCode());
                                        crudService.saveSubTask(subTask, DictId.TaskPerformStatus.ISSUED);
                                        issue = false;
                                    }
                                }else {
                                    // 解锁浅库位
                                    crudService.unlockLoc(outerLoc.getCode());
                                    issue = false;
                                }
                            }else {
                                crudService.unlockLoc(outerLoc.getCode());
                                issue = true;
                            }
                        }

                    }else {
                        issue = true;
                    }
                }else {
                    issue = true;
                }
            }
        }
        return issue;
    }

    private List<SubTask> getSortedSubTaskList(String parentTaskCode) throws BusinessException {
        Map<String, SubTask> ptm = new HashMap<>();
        List<SubTask> sortedSubTask = new ArrayList<>();
        List<SubTask> subTaskList = crudService.querySubTaskListByParentTask(parentTaskCode);
        subTaskList.forEach(subTask1 -> {
            ptm.put(subTask1.getForwardTask(), subTask1);
        });
        SubTask curSubTask = ptm.remove("");
        if(ObjectUtils.isEmpty(curSubTask))
            curSubTask = ptm.remove(null);

        sortedSubTask.add(curSubTask);
        while(!ptm.isEmpty()) {
            curSubTask = ptm.remove(curSubTask.getTaskCode());
            sortedSubTask.add(curSubTask);
        }
        return sortedSubTask;
    }

    /**
     * 执行子任务：根据机器人类型下发任务给相应的硬件设备
     *
     * @param subTask 需要执行的子任务对象
     * @throws BusinessException 业务异常
     *
     * 该方法根据子任务的机器人类型，将任务下发给对应的设备系统：
     * - CTU（穿梭车）任务下发给RCS系统
     * - AGV（自动导引车）任务下发给RCS系统
     * - 堆垛机任务直接通过WCS接口下发
     *
     * 任务成功下发后，更新子任务和主任务的状态为"执行中"
     * */
    @Override
    public void executeSubTask(SubTask subTask) throws BusinessException {
        boolean success = false;

        if(DictId.BotType.CTU.equals(subTask.getRobotType())) {
            // 下发穿梭车任务给RCS系统
            RCSResponse response = issueRCSCTUTask(subTask);
            if("0".equals(response.getCode())) {
                success = true;
            }
        }else if(DictId.BotType.AGV.equals(subTask.getRobotType())) {
            // 下发AGV任务给RCS系统
            RCSResponse response = issueRCSAgvTask(subTask);
            if("0".equals(response.getCode())) {
                success = true;
            }
        }else if(DictId.BotType.STACKER.equals(subTask.getRobotType())) {
            // 根据任务类型判断站台号
            TaskTypeEnum taskTypeEnum = TaskTypeEnum.getFromCode(subTask.getTaskType());
            if(ObjectUtils.isEmpty(taskTypeEnum)) {
                log.error("子任务类型不在期望中:taskCode[{}]", subTask.getTaskCode());
                throw new BusinessException("子任务类型不在期望中");
            }

            /** 校验是否允许下发任务，检查是否存在冲突或约束条件 */
            boolean allow = validateTaskCondition(subTask);

            if(allow) {
                // 构建堆垛机任务请求
                StackerCraneWCSTaskRequest request1 = new StackerCraneWCSTaskRequest();
                request1.setINSTRUCTION_CODE(subTask.getTaskCode());
                request1.setEND_LOCATION(subTask.getEndLoc());      // 目标库位
                request1.setTASK_TYPE(taskTypeEnum.getType());       // 任务类型
                request1.setPRI(3);                                  // 任务优先级
                request1.setSALVER_ID(subTask.getPodCode());         // 容器编号
                request1.setAREA_CODE("AREA1");                      // 区域代码
                request1.setSTART_LOCATION(subTask.getStartLoc());   // 起始库位

                // 根据任务类型设置站台号
                if(taskTypeEnum.getType() == 1)
                    request1.setSTATION_CODE(subTask.getStartLoc());     // 入库任务
                else if(taskTypeEnum.getType() == 2)
                    request1.setSTATION_CODE(subTask.getEndLoc());       // 出库任务
                else if(taskTypeEnum.getType() == 3) {
                    request1.setSTATION_CODE("S1_01");                 // 移库任务
                }

                // 添加任务明细
                List<StackerCraneWCSTaskRequest.TaskDetail> detailList = new ArrayList<>();
                StackerCraneWCSTaskRequest.TaskDetail detail1 = new StackerCraneWCSTaskRequest.TaskDetail();
                detail1.setMATERIAL_CODE("MAT");       // 物料代码
                detail1.setMATERIAL_DESC("");          // 物料描述
                detail1.setBATCH("");                  // 批次
                detail1.setQUANTITY(BigDecimal.ONE);   // 数量
                detailList.add(detail1);

                request1.setTASK_DETAIL_LIST(detailList);

                // 请求WCS生成堆垛机任务
                WCSResponse response = WCSTaskUtil.genAgvTask(request1);
                log.info("请求WCS生成堆垛机任务:request[{}],response[{}]", request1, response);
                if(ObjectUtils.isNotEmpty(response) && Integer.valueOf(ResultStatus.SUCCESS).equals(response.getRESULT())) {
                    if(ObjectUtils.isNotEmpty(response.getDATA()) && response.getDATA() instanceof List) {
                        JSONObject data = (JSONObject) ((JSONArray)response.getDATA()).get(0);
                        if(ResultStatus.SUCCESS == data.getInteger("SUCCESS")) {
                            success = true;
                            // 记录目标库位映射
                            targetLocationMap.put(request1.getINSTRUCTION_CODE(), request1.getEND_LOCATION());
                        }
                    }
                }
            }
        }else {
            log.info("子任务的机器人类型不在预期中.");
        }

        if(success) {    // 任务成功下发后，更新任务状态
            Map<String, Object> q = new HashMap<>();
            Map<String, Object> u = new HashMap<>();
            q.put("taskCode", subTask.getTaskCode());
            u.put("startTime", new Date());      // 设置开始时间
            u.put("dispatchTime", new Date());   // 设置下发时间
            u.put("performStatus", Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.RUNNING)));
            crudService.update(q, u, EntityTemplateId.SUB_TASK_TEMPLATE);

            // 如果是第一个子任务，同时更新主任务状态
            if(ObjectUtils.isEmpty(subTask.getForwardTask())) {
                q.put("taskCode", subTask.getParentTask());
                crudService.update(q, u, EntityTemplateId.TASK_TEMPLATE);
            }
        }
    }

    /** 下发潜伏式AGV任务 */
    public RCSResponse issueRCSAgvTask(SubTask subTask) throws BusinessException {
        TaskTypeEnum taskTypeEnum = TaskTypeEnum.getFromCode(subTask.getTaskType());

        RCSTaskRequest request = null;
        RCSResponse response = null;
        if(ObjectUtils.isNotEmpty(taskTypeEnum) && Integer.valueOf(4).equals(taskTypeEnum.getType())) {
            RCSTaskRequest.PositionCodeObj[] positionCodePath = new RCSTaskRequest.PositionCodeObj[2];
            positionCodePath[0] = new RCSTaskRequest.PositionCodeObj(subTask.getStartLoc(), RCSConstant.PositionType.GENERAL);
            positionCodePath[1] = new RCSTaskRequest.PositionCodeObj(subTask.getEndLoc(), RCSConstant.PositionType.GENERAL);

            String taskTyp = RCSConstant.TaskTemplate.AGV_TASK_TYP_OUT;

            request = RCSTaskRequest.builder()
                    .taskCode(subTask.getTaskCode())
//                    .taskTyp(RCSConstant.TaskTemplate.AGV_TASK_TYP)
                    .taskTyp(taskTyp)
//                    .podCode(subTask.getPodCode())
                    .positionCodePath(positionCodePath)
                    .build();
            response = RCSUtil.genAgvTask(request);
        }else if(ObjectUtils.isNotEmpty(taskTypeEnum) && Integer.valueOf(1).equals(taskTypeEnum.getType())) {
            RCSTaskRequest.PositionCodeObj[] positionCodePath = new RCSTaskRequest.PositionCodeObj[4];
            positionCodePath[0] = new RCSTaskRequest.PositionCodeObj(subTask.getStartLoc(), RCSConstant.PositionType.GENERAL);
            positionCodePath[1] = new RCSTaskRequest.PositionCodeObj(AgvRasterTaskEnum.getByStation(subTask.getEndLoc()).getOuterPos(),
                    RCSConstant.PositionType.GENERAL);
            positionCodePath[2] = new RCSTaskRequest.PositionCodeObj(subTask.getEndLoc(), RCSConstant.PositionType.GENERAL);
            positionCodePath[3] = new RCSTaskRequest.PositionCodeObj(AgvRasterTaskEnum.getByStation(subTask.getEndLoc()).getOuterPos(),
                    RCSConstant.PositionType.GENERAL);

            String taskTyp = RCSConstant.TaskTemplate.AGV_RASTER_IN;

            request = RCSTaskRequest.builder()
                    .taskCode(subTask.getTaskCode())
//                    .taskTyp(RCSConstant.TaskTemplate.AGV_TASK_TYP)
                    .taskTyp(taskTyp)
//                    .podCode(subTask.getPodCode())
                    .positionCodePath(positionCodePath)
                    .build();
            response = RCSUtil.genAgvTask(request);
        }else if(ObjectUtils.isNotEmpty(taskTypeEnum) && Integer.valueOf(2).equals(taskTypeEnum.getType())) {
            RCSTaskRequest.PositionCodeObj[] positionCodePath = new RCSTaskRequest.PositionCodeObj[4];
            positionCodePath[0] = new RCSTaskRequest.PositionCodeObj(AgvRasterTaskEnum.getByStation(subTask.getStartLoc()).getOuterPos(),
                    RCSConstant.PositionType.GENERAL);
            positionCodePath[1] = new RCSTaskRequest.PositionCodeObj(subTask.getStartLoc(), RCSConstant.PositionType.GENERAL);
            positionCodePath[2] = new RCSTaskRequest.PositionCodeObj(AgvRasterTaskEnum.getByStation(subTask.getStartLoc()).getOuterPos(),
                    RCSConstant.PositionType.GENERAL);
            positionCodePath[3] = new RCSTaskRequest.PositionCodeObj(subTask.getEndLoc(), RCSConstant.PositionType.GENERAL);


            String taskTyp = RCSConstant.TaskTemplate.AGV_RASTER_OUT;

            request = RCSTaskRequest.builder()
                    .taskCode(subTask.getTaskCode())
//                    .taskTyp(RCSConstant.TaskTemplate.AGV_TASK_TYP)
                    .taskTyp(taskTyp)
//                    .podCode(subTask.getPodCode())
                    .positionCodePath(positionCodePath)
                    .build();
            response = RCSUtil.genAgvTask(request);
        }

        log.info("请求RCS生成AGV任务:request[{}],response[{}]", request, response);
        return response;
    }

    public RCSResponse issueRCSCTUTask(SubTask subTask) throws BusinessException {
        RCSTaskTypEnum taskTypeEnum = RCSTaskTypEnum.getByTaskTypeCode(subTask.getTaskType());

        RCSTaskRequest request = null;
        RCSResponse response = null;
        if(ObjectUtils.isNotEmpty(taskTypeEnum) && taskTypeEnum.getName().equals("出库")) {
            RCSTaskRequest.PositionCodeObj[] posCodeArr = new RCSTaskRequest.PositionCodeObj[2];
            posCodeArr[0] = new RCSTaskRequest.PositionCodeObj(subTask.getStartLoc(), RCSConstant.PositionType.CTU_POSITION);
            posCodeArr[1] = new RCSTaskRequest.PositionCodeObj(subTask.getEndLoc(), RCSConstant.PositionType.CTU_POSITION);

            request = RCSTaskRequest.builder()
                    .reqCode(UUID.randomUUID().toString().replace("-", "").trim())
                    .taskCode(subTask.getTaskCode())
                    .taskTyp(RCSConstant.TaskTemplate.CTU_OUT_BOUND)
                    .positionCodePath(posCodeArr)
                    .groupId("100")
                    .build();

            response = RCSUtil.genAgvTask(request);
        }else if(ObjectUtils.isNotEmpty(taskTypeEnum) && taskTypeEnum.getName().equals("入库")) {
            RCSTaskRequest.PositionCodeObj[] posCodeArr = new RCSTaskRequest.PositionCodeObj[2];
            posCodeArr[0] = new RCSTaskRequest.PositionCodeObj(subTask.getStartLoc(), RCSConstant.PositionType.CTU_POSITION);
            posCodeArr[1] = new RCSTaskRequest.PositionCodeObj(subTask.getEndLoc(), RCSConstant.PositionType.CTU_POSITION);

            request = RCSTaskRequest.builder()
                    .reqCode(UUID.randomUUID().toString().replace("-", "").trim())
                    .taskCode(subTask.getTaskCode())
                    .taskTyp(RCSConstant.TaskTemplate.CTU_IN_BOUND)
                    .positionCodePath(posCodeArr)
                    .build();

            response = RCSUtil.genAgvTask(request);
        }else if(ObjectUtils.isNotEmpty(taskTypeEnum) && taskTypeEnum.getName().equals("移库")) {
            RCSTaskRequest.PositionCodeObj[] posCodeArr = new RCSTaskRequest.PositionCodeObj[2];
            posCodeArr[0] = new RCSTaskRequest.PositionCodeObj(subTask.getStartLoc(), RCSConstant.PositionType.CTU_POSITION);
            posCodeArr[1] = new RCSTaskRequest.PositionCodeObj(subTask.getEndLoc(), RCSConstant.PositionType.CTU_POSITION);

            request = RCSTaskRequest.builder()
                    .reqCode(UUID.randomUUID().toString().replace("-", "").trim())
                    .taskCode(subTask.getTaskCode())
                    .taskTyp(RCSConstant.TaskTemplate.CTU_TRANSPORT)
                    .positionCodePath(posCodeArr)
                    .build();

            return RCSUtil.genAgvTask(request);
        }

        log.info("请求RCS生成CTU任务:request[{}],response[{}]", request, response);
        return response;
    }

    /** 校验下发的任务是否满足条件 */
    private boolean validateTaskCondition(SubTask subTask) throws BusinessException {
        boolean isValid = true;
        /** 任务机器人类型为堆垛机 */
        if(DictId.BotType.STACKER.equals(subTask.getRobotType()) &&
                (DictId.TaskType.STOCK_OUT.equals(subTask.getTaskType()) || DictId.TaskType.MOVING.equals(subTask.getTaskType()))) {
            // 查询当前子任务起点是否为深层库位
            Map<String, Object> q = new HashMap<>();
            q.put("code", subTask.getStartLoc());
            Location loc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
            if(ObjectUtils.isNotEmpty(loc) && new Integer(2).equals(loc.getDeep())) {
                // 查询是否存在浅层库位
                q = new HashMap<>();
                q.put("deep", 1);
                q.put("group", loc.getGroup());
                Location outerLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                if(ObjectUtils.isNotEmpty(outerLoc)) {
                    // 查询浅层库位是否存在任务
                    q = new HashMap<>();
                    q.put("performStatus",Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.RUNNING)));

                    List<Map<String, Object>> orQList = new ArrayList<>();
                    Map<String, Object> orQ = new HashMap<>();
                    orQ.put("endLoc", outerLoc.getCode());
                    orQList.add(orQ);

                    orQ = new HashMap<>();                      //TODO 以浅层库位为起点的，未完成的任务（是否可省略）
                    orQ.put("startLoc", outerLoc.getCode());
                    orQList.add(orQ);

                    SubTask tempSubTask = crudService.findSubTaskOne(q, orQList);
                    if(ObjectUtils.isNotEmpty(tempSubTask)) {
                        log.info("浅层库位存在未完成的任务:taskCode[{}]", tempSubTask.getTaskCode());
                        isValid = false;
                    }

                    /** 查询浅层库位是否存在 以其为起点，状态为待下达或已下达的子任务 */
                    q = new HashMap<>();
                    q.put("startLoc", outerLoc.getCode());

                    orQList = new ArrayList<>();
                    orQ = new HashMap<>();
                    orQ.put("performStatus", Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.ISSUED)));
                    orQList.add(orQ);
                    orQ = new HashMap<>();
                    orQ.put("performStatus", Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.UNISSUED)));
                    orQList.add(orQ);

                    tempSubTask = crudService.findSubTaskOne(q, orQList);
                    if(ObjectUtils.isNotEmpty(tempSubTask)) {
                        log.info("浅层库位存在未完成的任务:taskCode[{}]", tempSubTask.getTaskCode());
                        isValid = false;
                    }
                }
            }
        }
        if(DictId.BotType.STACKER.equals(subTask.getRobotType()) &&
                (DictId.TaskType.STOCK_IN.equals(subTask.getTaskType()) || DictId.TaskType.MOVING.equals(subTask.getTaskType()))) {
            // 查询当前子任务终点是否为深层库位
            Map<String, Object> q = new HashMap<>();
            q.put("code", subTask.getEndLoc());
            Location loc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
            if(ObjectUtils.isNotEmpty(loc) && new Integer(1).equals(loc.getDeep())) {
                // 查询是否存在深层库位
                q = new HashMap<>();
                q.put("deep", 2);
                q.put("group", loc.getGroup());
                Location innerLoc = crudService.findOne(q, Location.class, EntityTemplateId.LOC_TEMPLATE);
                if(ObjectUtils.isNotEmpty(innerLoc)) {
                    // 查询浅层库位是否存在任务
                    q = new HashMap<>();
                    q.put("performStatus",Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.RUNNING)));

                    List<Map<String, Object>> orQList = new ArrayList<>();
                    Map<String, Object> orQ = new HashMap<>();      //TODO 以深层库位为终点的，未完成的任务（是否可省略）
                    orQ.put("endLoc", innerLoc.getCode());
                    orQList.add(orQ);

                    orQ = new HashMap<>();
                    orQ.put("startLoc", innerLoc.getCode());
                    orQList.add(orQ);

                    SubTask tempSubTask = crudService.findSubTaskOne(q, orQList);
                    if(ObjectUtils.isNotEmpty(tempSubTask)) {
                        log.info("深层库位存在未完成的任务:taskCode[{}]", tempSubTask.getTaskCode());
                        isValid = false;
                    }

                    /** 查询浅层库位是否存在 以其为起点，状态为待下达或已下达的子任务 */
                    q = new HashMap<>();
                    q.put("endLoc", innerLoc.getCode());

                    orQList = new ArrayList<>();
                    orQ = new HashMap<>();
                    orQ.put("performStatus", Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.ISSUED)));
                    orQList.add(orQ);
                    orQ = new HashMap<>();
                    orQ.put("performStatus", Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.UNISSUED)));
                    orQList.add(orQ);

                    tempSubTask = crudService.findSubTaskOne(q, orQList);
                    if(ObjectUtils.isNotEmpty(tempSubTask)) {
                        log.info("深层库位存在未完成的任务:taskCode[{}]", tempSubTask.getTaskCode());
                        isValid = false;
                    }
                }
            }

        }
        return isValid;
    }

    @Override
    @Async
    public void executeNextSubTask(String subTaskCode) throws BusinessException {
        List<AggregationOperation> operations = new ArrayList<>();
        operations.add(Aggregation.match(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())
                .and("forwardTask").is(subTaskCode)));

        operations.add(Aggregation.addFields()
                .addFieldWithValue("robotType", ConvertOperators.ToString.toString("$robotType.id"))
                .addFieldWithValue("performStatus", ConvertOperators.ToString.toString("$performStatus.id"))
                .addFieldWithValue("taskType", ConvertOperators.ToString.toString("$taskType.id"))
                .build());
        List<SubTask> subTaskList = mongoTemplate.aggregate(Aggregation.newAggregation(operations), EntityTemplateId.SUB_TASK_TEMPLATE, SubTask.class).getMappedResults();

        log.info("查找到当前任务的下一任务如下:forwardTask[{}],nextTaskListSize[{}]", subTaskCode, subTaskList.size());
        subTaskList.forEach(subTask -> log.info(subTask.toString()));

        executeSubTask(subTaskList);
    }

    @Override
//    @Async
    public void notifyTaskExecResult(String subTaskCode, String action) throws BusinessException {
        taskNotifyService.notifyTaskExecResult(subTaskCode, action);
    }

    /** 通知上层业务服务发生了移库动作 */
    @Override
//    @Async
    public void notifyMovingTaskAction(String subTaskCode, String action) throws BusinessException {
        taskNotifyService.notifyMovingTaskAction(subTaskCode, action);
    }

    @Override
    public void updateTaskStatus(String taskCode, String action) throws BusinessException {
        if("finish".equals(action)) {
            Map<String, Object> q = new HashMap<>();
            q.put("taskCode", taskCode);

            Map<String, Object> u = new HashMap<>();
            u.put("finishTime", new Date());
            u.put("performStatus",  Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.FINISHED)));
            long mc = crudService.update(q, u, EntityTemplateId.SUB_TASK_TEMPLATE);
            log.info("更新子任务状态:taskCode[{}],performStatus[{}],res[{}]", taskCode, u, mc);

            q = new HashMap<>();
            q.put("forwardTask", taskCode);
            boolean existed = crudService.exists(q, EntityTemplateId.SUB_TASK_TEMPLATE);
            if(!existed) {
                SubTask subTask = crudService.querySubTaskByTaskCode(taskCode);
                q = new HashMap<>();
                q.put("taskCode", subTask.getParentTask());

                u = new HashMap<>();
                u.put("finishTime", new Date());
                u.put("performStatus",  Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.FINISHED)));
                mc = crudService.update(q, u, EntityTemplateId.TASK_TEMPLATE);
                log.info("更新任务状态:taskCode[{}],performStatus[{}],res[{}]", subTask.getParentTask(), u, mc);
            }
        }else if("outbin".equals(action)) {

        }
    }

    private void notifyTaskExecResult(String taskCode, String method, String podCode, String curPosCode) throws BusinessException {
        WCSTaskCallbackVO callbackVO = new WCSTaskCallbackVO();
        callbackVO.setTaskCode(taskCode);
        callbackVO.setPodCode(podCode);
        callbackVO.setMethod(method);
        callbackVO.setCurrentPositionCode(curPosCode);

    }

    private static HttpResult sendRequest(WCSTaskCallbackVO callbackVO, String url) {
        String jsonData = JSONObject.toJSONString(callbackVO);
        return HttpClientUtil.getInstance().postJson(url, jsonData);
    }

    private static HttpResult sendRequest(WCSTaskCallbackVO.MovingTaskCallbackVO callbackVO, String url) {
        String jsonData = JSONObject.toJSONString(callbackVO);
        return HttpClientUtil.getInstance().postJson(url, jsonData);
    }

    @Override
//    @Async
    public void updateTaskResource(String subTaskCode, String action) throws BusinessException {
        SubTask subTask = crudService.querySubTaskByTaskCode(subTaskCode);
        if(ObjectUtils.isEmpty(subTask))
            return;

        Map<String, SubTask> ptm = new HashMap<>();
        List<SubTask> sortedSubTask = new ArrayList<>();
        List<SubTask> subTaskList = crudService.querySubTaskListByParentTask(subTask.getParentTask());
        subTaskList.forEach(subTask1 -> {
            ptm.put(subTask1.getForwardTask(), subTask1);
        });
        SubTask curSubTask = ptm.remove("");
        if(ObjectUtils.isEmpty(curSubTask))
            curSubTask = ptm.remove(null);

        sortedSubTask.add(curSubTask);
        while(!ptm.isEmpty()) {
            curSubTask = ptm.remove(curSubTask.getTaskCode());
            sortedSubTask.add(curSubTask);
        }

        if(sortedSubTask.size() >= 2) {
            for(int i=1; i<sortedSubTask.size(); ++i) {
                long mc = crudService.unlockLoc(sortedSubTask.get(i).getStartLoc());
                log.info("解锁中间点库位:taskCode[{}],locCode[{}],res[{}]", subTask.getParentTask(), sortedSubTask.get(i).getStartLoc(), mc);
            }
        }
    }

    /** 继续执行任务
     *  查询前置任务不为空，且状态为已下达的子任务
     *  按顺序尝试执行查询到的子任务
     * */
    @Override
    public void continuePerformTask() throws BusinessException {
        // 查询forwardTask不为空，且performStatus为待执行的子任务
        List<AggregationOperation> operations = new ArrayList<>();
        operations.add(Aggregation.match(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())
                .and("forwardTask").nin(null, "")
                .and("performStatus").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.ISSUED)))));

        operations.add(Aggregation.addFields()
                .addFieldWithValue("robotType", ConvertOperators.ToString.toString("$robotType.id"))
                .addFieldWithValue("performStatus", ConvertOperators.ToString.toString("$performStatus.id"))
                .addFieldWithValue("taskType", ConvertOperators.ToString.toString("$taskType.id"))
                .build());

        operations.add(Aggregation.lookup(EntityTemplateId.SUB_TASK_TEMPLATE, "forwardTask", "taskCode", "forwardSubTask"));
        operations.add(Aggregation.unwind("forwardSubTask", true));

        operations.add(Aggregation.match(Criteria.where("forwardSubTask.tenantId").is(tenantId).and("forwardSubTask.isDelete").ne(YesOrNo.YES.ordinal())
                .and("forwardSubTask.performStatus").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.FINISHED)))));

        List<SubTask> subTaskList = mongoTemplate.aggregate(Aggregation.newAggregation(operations), EntityTemplateId.SUB_TASK_TEMPLATE, SubTask.class).getMappedResults();
        if(ObjectUtils.isNotEmpty(subTaskList)) {
            for(SubTask subTask: subTaskList) {

//                executeSubTask(subTask);
                try {
                    /** 执行冲突解决 */
                    List<SubTask> sortedSubTaskList = getSortedSubTaskList(subTask.getParentTask());
                    boolean issue = resolveConflict(sortedSubTaskList);
                    if(issue) {
                        log.info("尝试执行任务:[{}]", subTask);
                        executeSubTask(subTask);
                    }
                } catch (Exception e) {
                    log.error("请求执行子任务抛出异常: {}", e);
                }
            }
        }
    }


    private SubTask findLastSubTaskOfTask(List<SubTask> subTaskList) {
        SubTask subTask = null;
        for(int i=0; i<subTaskList.size(); ++i) {
            boolean last = true;
            for (SubTask task : subTaskList) {
                if (subTaskList.get(i).getTaskCode().equals(task.getForwardTask())) {
                    last = false;
                    break;
                }
            }
            if(last)
                subTask = subTaskList.get(i);
        }
        return subTask;
    }

    public static Map<String, String> targetLocationMap = new ConcurrentHashMap<>();

    @Override
    public Object genWCSTask(WCSTaskRequest request) throws BusinessException {
        StackerCraneWCSTaskRequest request1 = new StackerCraneWCSTaskRequest();
        request1.setINSTRUCTION_CODE(UUID.randomUUID().toString().replaceAll("-", ""));
        request1.setEND_LOCATION(request.getEndLocation());
        request1.setTASK_TYPE(request.getTaskType());
        request1.setPRI(request.getPri());
        request1.setSALVER_ID(request.getSalverId());
        request1.setAREA_CODE(request.getAreaCode());
        request1.setSTART_LOCATION(request.getStartLocation());
        request1.setSTATION_CODE(request.getStationCode());
        List<StackerCraneWCSTaskRequest.TaskDetail> detailList = new ArrayList<>();
        for(WCSTaskRequest.TaskDetail detail: request.getTaskDetailList()) {
            StackerCraneWCSTaskRequest.TaskDetail detail1 = new StackerCraneWCSTaskRequest.TaskDetail();
            detail1.setMATERIAL_CODE(detail.getMaterialCode());
            detail1.setMATERIAL_DESC(detail.getMaterialDesc());
            detail1.setBATCH(detail.getBatch());
            detail1.setQUANTITY(BigDecimal.ONE);
            detailList.add(detail1);
        }
        targetLocationMap.put(request1.getINSTRUCTION_CODE(), request1.getEND_LOCATION());
        request1.setTASK_DETAIL_LIST(detailList);
        return WCSTaskUtil.genAgvTask(request1);
    }

    @Override
    public Object genWCSTaskMulti(List<WCSTaskRequest> list) throws BusinessException {
        List<StackerCraneWCSTaskRequest> list1 = new ArrayList<>();
        for(WCSTaskRequest request: list) {
            StackerCraneWCSTaskRequest request1 = new StackerCraneWCSTaskRequest();
            request1.setINSTRUCTION_CODE(UUID.randomUUID().toString().replaceAll("-", ""));
            request1.setEND_LOCATION(request.getEndLocation());
            request1.setTASK_TYPE(request.getTaskType());
            request1.setPRI(request.getPri());
            request1.setSALVER_ID(request.getSalverId());
            request1.setAREA_CODE(request.getAreaCode());
            request1.setSTART_LOCATION(request.getStartLocation());
            request1.setSTATION_CODE(request.getStationCode());
            List<StackerCraneWCSTaskRequest.TaskDetail> detailList = new ArrayList<>();
            for(WCSTaskRequest.TaskDetail detail: request.getTaskDetailList()) {
                StackerCraneWCSTaskRequest.TaskDetail detail1 = new StackerCraneWCSTaskRequest.TaskDetail();
                detail1.setMATERIAL_CODE(detail.getMaterialCode());
                detail1.setMATERIAL_DESC(detail.getMaterialDesc());
                detail1.setBATCH(detail.getBatch());
                detail1.setQUANTITY(BigDecimal.ONE);
                detailList.add(detail1);
            }
            request1.setTASK_DETAIL_LIST(detailList);
            targetLocationMap.put(request1.getINSTRUCTION_CODE(), request1.getEND_LOCATION());
            list1.add(request1);
        }

        return WCSTaskUtil.genAgvTask(list1);
    }

    @Override
    public Object genCTUTransportTask(TaskVO taskVO) throws BusinessException {
        RCSTaskRequest.PositionCodeObj[] posCodeArr = new RCSTaskRequest.PositionCodeObj[2];
        posCodeArr[0] = new RCSTaskRequest.PositionCodeObj(taskVO.getStartLoc(), RCSConstant.PositionType.CTU_POSITION);
        posCodeArr[1] = new RCSTaskRequest.PositionCodeObj(taskVO.getEndLoc(), RCSConstant.PositionType.CTU_POSITION);

        RCSTaskRequest request = RCSTaskRequest.builder()
                .reqCode(UUID.randomUUID().toString().replace("-", "").trim())
                .taskTyp(RCSConstant.TaskTemplate.CTU_TRANSPORT)
                .positionCodePath(posCodeArr)
                .build();

//        builder.ctnrTyp("1");
        return RCSUtil.genAgvTask(request);
    }

    @Override
    public Object genCTUOutboundTask(RCSTaskRequestVO requestVO) throws BusinessException {
        RCSTaskRequest.PositionCodeObj[] posCodeArr = new RCSTaskRequest.PositionCodeObj[2];
        posCodeArr[0] = new RCSTaskRequest.PositionCodeObj(requestVO.getStartLoc(), RCSConstant.PositionType.CTU_POSITION);
        posCodeArr[1] = new RCSTaskRequest.PositionCodeObj(requestVO.getEndLoc(), RCSConstant.PositionType.CTU_POSITION);

        RCSTaskRequest request = RCSTaskRequest.builder()
                .reqCode(UUID.randomUUID().toString().replace("-", "").trim())
                .taskTyp(RCSConstant.TaskTemplate.CTU_OUT_BOUND)
                .positionCodePath(posCodeArr)
                .build();
        if(ObjectUtils.isNotEmpty(requestVO.getGroupId())) {
            request.setGroupId(requestVO.getGroupId());
        }
        if(ObjectUtils.isNotEmpty(requestVO.getAgvCode())) {
            request.setAgvCode(request.getAgvCode());
        }

//        builder.ctnrTyp("1");
        return RCSUtil.genAgvTask(request);
    }

    @Override
    public Object genCTUInboundTask(RCSTaskRequestVO requestVO) throws BusinessException {
        RCSTaskRequest.PositionCodeObj[] posCodeArr = new RCSTaskRequest.PositionCodeObj[2];
        posCodeArr[0] = new RCSTaskRequest.PositionCodeObj(requestVO.getStartLoc(), RCSConstant.PositionType.CTU_POSITION);
        posCodeArr[1] = new RCSTaskRequest.PositionCodeObj(requestVO.getEndLoc(), RCSConstant.PositionType.CTU_POSITION);

        RCSTaskRequest request = RCSTaskRequest.builder()
                .reqCode(UUID.randomUUID().toString().replace("-", "").trim())
                .taskTyp(RCSConstant.TaskTemplate.CTU_IN_BOUND)
                .positionCodePath(posCodeArr)
                .build();
        if(ObjectUtils.isNotEmpty(requestVO.getGroupId())) {
            request.setGroupId(requestVO.getGroupId());
        }
        if(ObjectUtils.isNotEmpty(requestVO.getAgvCode())) {
            request.setAgvCode(requestVO.getAgvCode());
        }

//        builder.ctnrTyp("1");
        return RCSUtil.genAgvTask(request);
    }

    @Override
    public Object boxApplyPass(RCSTaskRequestVO requestVO) throws BusinessException {
        RCSTaskRequest request = RCSTaskRequest.builder()
                .reqCode(UUID.randomUUID().toString().replace("-", "").trim())
                .taskCode(requestVO.getTaskCode())
                .type(requestVO.getType())
                .build();

//        builder.ctnrTyp("1");
        return RCSUtil.boxApplyPass(request);
    }

    @Override
    public Object requestOpenRaster(AGVRasterRequest request) throws BusinessException {
        return WCSTaskUtil.openRaster(request);
    }

    @Override
    public Object requestCloseRaster(AGVRasterRequest request) throws BusinessException {
        return WCSTaskUtil.closeRaster(request);
    }

    @Override
    public void releaseResource(WCSTaskCallbackVO callbackVO) throws BusinessException {
        Task task = crudService.queryTaskByTaskCode(callbackVO.getTaskCode());
        if(ObjectUtils.isEmpty(task))
            return;
        if("outbin".equals(callbackVO.getMethod())) {
            crudService.unlockLoc(task.getStartLoc());

            Map<String, Object> u = new HashMap<>();
            u.put("containerCode", "");
            crudService.updateLocationByLocCode(task.getStartLoc(), u);
        }else if("finish".equals(callbackVO.getMethod())) {
            crudService.unlockLoc(task.getEndLoc());

            Map<String, Object> u = new HashMap<>();
            u.put("containerCode", task.getPodCode());
            crudService.updateLocationByLocCode(task.getEndLoc(), u);
        }
    }

    @Override
    public boolean existRunningOutBoundTaskForCTU(Object data) {
        Query query = new Query(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())
                .and("robotType").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.BotType.CTU)))
                .and("taskType").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskType.STOCK_OUT)))
                .and("performStatus").is(Document.parse(String.format("{id: ObjectId(\"%s\")}", DictId.TaskPerformStatus.RUNNING))));

        return mongoTemplate.exists(query, EntityTemplateId.SUB_TASK_TEMPLATE);
    }

    public SubTask createSubTask(String areaCode, String taskType, String parentTaskCode, String podCode, String startLoc, String endLoc, String startArea, String endArea) throws BusinessException {
        Area area = crudService.queryAreaByCode(areaCode);
        RobotTypeEnum taskTemplateEnum = RobotTypeEnum.getByAreaAutoType(area.getAutoType());
        if(ObjectUtils.isEmpty(taskTemplateEnum)) {
            throw new BusinessException("子任务机器人类型配置为空");
        }

        SubTask subTask = new SubTask();
        subTask.setTaskCode(UUID.randomUUID().toString().replaceAll("-", ""));
        subTask.setParentTask(parentTaskCode);
        subTask.setRobotType(taskTemplateEnum.getBotType());     // 机器人类型（根据库区类型决定）
        subTask.setPerformStatus(DictId.TaskPerformStatus.UNISSUED);    // 任务执行状态
        subTask.setStartLoc(startLoc);              // 起点库位
        subTask.setEndLoc(endLoc);                  // 终点库位
        subTask.setStartArea(startArea);            // 起点库区
        subTask.setEndArea(endArea);                // 终点库区
        subTask.setPodCode(podCode);    // 容器编号
        subTask.setTaskType(taskType);

//        crudService.saveSubTask(subTask);
        return subTask;
    }
}
